#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""sycn data"""

import time
import os
import sys
import pycurl
import redis
import pymongo
import re
import codecs
import pro_rank

class DataSync(object):
    """
    Data sync in erver day
    1.download redis rdb from url
    2.stop redis server
    3.restore redis data
    4.restart redis server
    5.analyze redis data to mongodb
    """
    contents = ''
    progress = 'init'
    day = 0
    start_time = 0
    end_time = 0
    timeout = 300
    # proc = {
    #    'init': 0,
    #    'down_rdb': 20,
    #    'wait_redis': 30,
    #    'analyze_redis': 50,
    #    'finish': 100
    # }
    # defined data sync proc state value
    proc = dict(init=0, down_rdb=20, wait_redis=30, analyze_redis=50, rank=80, finish=100)
    # defined used lang
    lang = {
        'init': u'系统初始化...',
        'down_rdb': u'下载Redis数据库...',
        'wait_redis': u"Redis加载数据...",
        'analyze_redis': u"整理Redis数据...",
        'rank': u'统计排名信息...',
        'finish': u'同步已完成'
    }

    def __init__(self):
        """init var"""
        self.m = pymongo.MongoClient('127.0.0.1', 27017)["fuwa"]
        # self.m = self.m["fuwa"]
        pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0)
        self.r = redis.Redis(connection_pool=pool)
        self.rdb_url = "http://120.25.99.102/dump.rdb"  # redis rdb
        self.day = int(time.strftime("%Y%m%d", time.localtime(time.time())))
        self.progress = 'init'

    def log(self, msg=u'info'):
        """run log"""
        logname = 'sync.log'
        timestr = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        with codecs.open(os.path.join(os.path.curdir, logname), 'a+', 'utf-8') as f:
            f.write('%s %s\n' % (timestr, msg))

    def start(self):
        """sync start"""
        self.start_time = time.time()
        print "sync start in: %s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.start_time))
        # sync state
        if not self.get_sync_state():
            try:
                print self.lang.get(self.progress)
            except UnicodeEncodeError:
                print codecs.utf_8_encode(self.lang.get(self.progress))
            self.log(self.lang.get(self.progress))
            return
        self.task(str(self.progress))

    def get_sync_state(self):
        """
         get current day data sync state
         :return :bool
         True is need sync
         False is finished
        """
        # l = self.m["sync"].find().sort([("sync_time", -1)]).limit(1)
        l = self.m.get_collection("sync").find().sort([("sync_time", -1)]).limit(1)
        # sync = None
        # for i in l:
        #    sync = i
        #    break
        try:
            sync = l[0]
        except IndexError:
            return True
        if not sync:
            return True
        if self.day == int(sync["sync_time"]):
            # current day data sync finished
            if sync["progress"] == 'finish':
                self.progress = sync["progress"]
                return False
            self.progress = sync["progress"]
        return True

    def proc_init(self):
        """proc_init"""
        self.task("down_rdb")

    def proc_down_rdb(self):
        """proc down"""
        print 'download from : %s' % self.rdb_url
        curl = pycurl.Curl()
        curl.setopt(curl.URL, self.rdb_url)
        curl.setopt(curl.WRITEFUNCTION, self.save_buf)
        try:
            curl.perform()
            msg = 'Get from %s-status code:%s' % (curl.getinfo(pycurl.EFFECTIVE_URL), curl.getinfo(pycurl.HTTP_CODE))
            self.log(msg)
            print msg
            curl.close()
            msg = 'Length of contents: %f M' % (len(self.contents) / 1024 / 1024)
            self.log(msg)
            print msg
            self.save_redis()
        except pycurl.error as e:
            print e
            msg = '5s reconnection to %s' % curl.getinfo(pycurl.EFFECTIVE_URL)
            self.log(msg)
            print msg
            self.timeout -= 5
            if self.timeout <= 0:
                raise pycurl.error('Failed to connect to %s: Connection refused' % curl.getinfo(pycurl.EFFECTIVE_URL))
            time.sleep(5)
            self.task("down_rdb")
        self.task("wait_redis")

    def proc_wait_redis(self):
        """waiting redis loading"""
        while 1:
            try:
                # query redis database state
                self.r.get("globalid")
                break
            except Exception:
                time.sleep(5)
        self.task("analyze_redis")

    def proc_analyze_redis(self):
        """analyze redis"""
        # fuwa_count = int(self.r.get("globalid"))
        fuwa_i_hide = self.r.zcard("fuwa_i")
        fuwa_c_hide = self.r.zcard("fuwa_c")
        fuwa_i = 0
        fuwa_c = 0
        fuwa_i_caught = 0
        fuwa_c_caught = 0
        gender = 0
        man = 0
        weman = 0
        awarded = 0
        unaward = 0
        is_fuwa = False
        fuwa_id_num = dict()
        for k in self.r.keys():
            key = str(k)
            p = re.match(r'^([0-9]+)_apply$', key)
            tzset = re.match(r'^([0-9]+)_pack$', key)
            if len(key) > 6:
                # exclude fuwa_i_userid and fuwa_c_userid
                if key[0:7] == 'fuwa_i_' or key[0:7] == 'fuwa_c_':
                    try:
                        self.r.hget(key, "owner")
                    except:
                        continue
                if key[0:7] == 'fuwa_i_':
                    fuwa_i += 1
                    if self.r.hget(key, "owner") != self.r.hget(key, "creator"):
                        fuwa_i_caught += 1
                        self.r.zadd('fuwa_cau_i', key, 1)
                    is_fuwa = True
                    # print key, fuwa_i
                elif key[0:7] == 'fuwa_c_':
                    fuwa_c += 1
                    if self.r.hget(key, "owner") != self.r.hget(key, "creator"):
                        fuwa_c_caught += 1
                        self.r.zadd('fuwa_cau_c', key, 1)
                    is_fuwa = True
                    # print key, fuwa_c
                elif p:
                    # apply list
                    apply_key = '%s_apply' % p.group(1)
                    self.r.zadd('fuwa_apply_user', apply_key, self.r.scard(apply_key))
                    t = self.r.smembers(apply_key)
                    for i in t:
                        self.r.zadd('fuwa_apply', i, 1)
                        # print i
                    # print key
                elif tzset:
                    pack_key = '%s_pack' % tzset.group(1)
                    self.r.zadd('fuwa_rank', tzset.group(1), self.r.scard(pack_key))

                if is_fuwa:
                    is_fuwa = False
                    if self.r.hget(key, "gender") == "男":
                        man += 1
                    elif self.r.hget(key, "gender") == "女":
                        weman += 1
                    gender += 1
                    # write user key
                    t_k = '%s_fuwas' % self.r.hget(key, 'creator')
                    self.r.zadd(t_k, key, 1)
                    if self.r.hget(key, "awarded") == '1':
                        awarded += 1
                        self.r.zadd('fuwa_awarded', key, '1')
                        creator = self.r.hget(key, 'creator')
                        num = self.r.zscore('user_awarded', creator)
                        if not num:
                            num = 1
                        else:
                            num += 1
                        self.r.zadd('user_awarded', creator, num)
                    else:
                        unaward += 1
                    # fuwa number total
                    fuwa_id = '%s' % self.r.hget(key, 'id')
                    if fuwa_id_num.get(fuwa_id):
                        fuwa_id_num[fuwa_id] += 1
                    else:
                        fuwa_id_num[fuwa_id] = 1
        # total user caut
        cautkyes = ('fuwa_cau_c', 'fuwa_cau_i')
        for ckey in cautkyes:
            fuwas = self.r.zrange(ckey, 0, self.r.zcard(ckey))
            for tfuwa in fuwas:
                # print tfuwa
                creator = self.r.hget(tfuwa, 'creator')
                num = self.r.zscore('user_caut', creator)
                if not num:
                    num = 1
                else:
                    num += 1
                self.r.zadd('user_caut', creator, num)
        # total hide
        hidekeys = ('fuwa_i', 'fuwa_c')
        for hkey in hidekeys:
            fuwas = self.r.zrange(hkey, 0, self.r.zcard(hkey))
            for tfuwa in fuwas:
                # print tfuwa
                creator = self.r.hget(tfuwa, 'creator')
                num = self.r.zscore('user_hide', creator)
                if not num:
                    num = 1
                else:
                    num += 1
                self.r.zadd('user_hide', creator, num)
        fuwa = {
            "fu_count": gender,
            "fu_i_count": fuwa_i,
            "fu_c_count": fuwa_c,
            "fu_i_hide": fuwa_i_hide,
            "fu_c_hide": fuwa_c_hide,
            "fu_i_caught": fuwa_i_caught,
            "fu_c_caught": fuwa_c_caught,
            "man": man,
            "weman": weman,
            "gender": gender,
            "awarded": awarded,
            "unaward": unaward,
            "fuwa_id_num": fuwa_id_num,
            "sync_time": time.time()
        }
        self.m["view"].save(fuwa)
        self.log(fuwa.__str__())
        print fuwa
        self.task('rank')

    def proc_rank(self):
        """pro rank"""
        pro_rank.get_ranks()
        self.task('finish')

    def proc_finish(self):
        """print finish time"""
        msg = 'Time taken for task: %s' % (time.time() - self.start_time)
        self.log(msg)
        print msg

    def task(self, p):
        """
        run task with p
        :param p: task state
         see self.progress key
        """
        try:
            print self.lang.get(str(p))  # print current task state
        except UnicodeEncodeError:
            print codecs.utf_8_encode(self.lang.get(str(p)))
        self.log(self.lang.get(str(p)))
        self.set_sync_state(p)  # set task exce current state
        eval('self.proc_' + str(p) + '()')  # run current state

    def save_buf(self, res):
        """
        curl save to self buffer
        :param res: string 
        """
        self.contents = self.contents + res

    def set_sync_state(self, progress):
        """
        set sync state
        :param progress: string see self.progress key
        """
        # query sync in mongodb
        l = self.m["sync"].find_one({"sync_time": self.day})
        if not l:
            l = dict()
            l["sync_time"] = self.day
        l["progress"] = progress
        # save sync state to mongodb
        self.m["sync"].save(l)
        # update self current sync state
        self.progress = progress

    def save_redis(self):
        """write self.buffer contents to redis db"""
        # MacOS
        if os.uname()[0] == 'Darwin':
            stop_cmd = "brew services stop redis"
            start_cmd = "brew services start redis"
            redis_db = "/usr/local/var/db/redis/dump.rdb"
        else:
            stop_cmd = '/bin/systemctl stop  redis.service'
            start_cmd = '/bin/systemctl start redis.service'
            redis_db = '/var/lib/redis/dump.rdb'
        # execute os shell
        os.system(stop_cmd)
        self.log('Run:%s' % stop_cmd)
        # delete rdb
        os.system("rm -f %s" % redis_db)
        self.log('Run: rm -f %s' % redis_db)
        # write contents to redis_db file
        with open(redis_db, 'wb') as f:
            f.write(self.contents)
        self.contents = None
        time.sleep(3)
        os.system(start_cmd)
        self.log('Run: %s' % start_cmd)
        return True

if __name__ == '__main__':
    d = DataSync()
    d.start()
